#include <string.h>
#include <stdio.h>

#include "lib.h"
#include "CPokerServer.h"
#include "CPokerConnection.h"
#include "cshand.h"
#include "cssql.h"
#include "CSavedGame.h"
#include "csgame.h"
#include "csproto.h"
#include "stdncerr.h"
#include "opts.h"
#include "log.h"
#include "CHoldemSQL.h"
#include "XMLTranslator.h"

// [AO 2008-03-28] Draw poker was missing
#include "cdrawsql.h"

#include "growbuf.h"


#define APP1		(uint8)'C'
#define APP2		(uint8)'S'


void
CPokerServer::startTracker()
{
	ServerOpts::load();

	tracker = new ProgressiveTrackSQL(ServerOpts::dbdsn, ServerOpts::dblogin, 
                                      ServerOpts::dbpasswd);

	// CSN-8891
	webServiceCall = new WebServiceCall();
}

void
CPokerServer::stopTracker()
{
	delete tracker;
	tracker = 0;

	delete webServiceCall;
	webServiceCall = 0;
}

bool8
CPokerServer::initSuccess()
{
	if (tracker && tracker->initOK())
		return Server::initSuccess();
	else
	{
		Log::msgError("Failed to connect to the database.  Aborting.");
		return false;
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CPokerConnection::getDBInterface(CaribbeanMachineId eMachineID)
///
/// Returns the appropriate type of game object for the provided machine id.
///
/// @param eMachineID       indicates which game is being played
///
/// @return CDBInterface*   returns a pointer to a new instance of the appropriate 
///                         game database interface
///
////////////////////////////////////////////////////////////////////////////////////////////////
CDBInterface * CPokerConnection::getDBInterface(CaribbeanGameId eGameID)
{   
    switch(eGameID)
    {
        case CARIBBEAN_STUD:
        {
            return new CStudSQL(tracker, m_pInfo);
        }
        case CARIBBEAN_HOLDEM:
        {
            return new CHoldemSQL(tracker, m_pInfo);
        }

		// [AO 2008-03-28] Draw poker was missing
		case CARIBBEAN_DRAW:
		{
				return new CDrawSQL(tracker, m_pInfo);
		}

        default:
        {
            Log::msgError("Unknown game type.");
            return NULL;
        }
    }
}

bool8 CPokerConnection::getForRealFlag()
{
    if (m_pInfo && m_pInfo->forMoney)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool8
CPokerConnection::parseAnsiCasinoLogin(int32* Err)
{
	int32 i=0;

    ASSERT( msgbuf[i] == 'L' ); i++;

    unsigned char byVer2;

	if (msgbuf[i++]	!= APP1				|| msgbuf[i++] != APP2 ||
		msgbuf[i++]	!= PROTO_VERSION1   || (byVer2 = msgbuf[i++]) > PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c %c%c. Looking for %c%c.%c%c",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4], APP1, APP2, PROTO_VERSION1, PROTO_VERSION2));
		return false;
	}
    

    //  kharmon.  09-28-05.  #5861.  Now include a machine id in the login message.
    if( byVer2 < PROTO_VERSION2)
    {
        m_eGameId = CARIBBEAN_STUD;
    }
    else if(byVer2 == PROTO_VERSION2)
    {
        m_eGameId = (CaribbeanGameId) msgbuf[i++];
    }

	// [AO 2008-03-28] Draw poker was missing
	if(m_eGameId != CARIBBEAN_STUD && m_eGameId != CARIBBEAN_HOLDEM && m_eGameId != CARIBBEAN_DRAW)
    {
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: invalid machine id: %d", (int) m_eGameId));
		return false;
    }

	m_pInfo = new CInfo();

	if (!m_pInfo)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	char login[PIDLEN + 1];
	memcpy(login, msgbuf + i, PIDLEN);
	login[PIDLEN] = '\0';
	i += PIDLEN;

	strlwr(login);
	m_pInfo->login = login;

	m_pInfo->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( m_pInfo->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}
	else
	{
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, m_pInfo->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					m_pInfo->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				return false;
			}
		}
	}

    memset(m_pInfo->gid, 0xff, sizeof(CPokerGameID));
  
	// Create RTG Session
	if (tracker)
	{
		bool8 result;
		tracker->lock();
		result = tracker->createRTGSession(m_pInfo->forMoney, m_pInfo->login, m_pInfo->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}    
	
	m_pInfo->pid = m_pInfo->login;
	m_pDBInterface = getDBInterface(m_eGameId);

	/*
	m_pDBInterface = getDBInterface(m_eGameId);
	int32 result = m_pDBInterface->trackValidateLogin();

	if(m_pDBInterface==NULL || result != TrackSQL::NoError)
	{
		if(result == TrackSQL::AdminLogout)
			*Err = LOGINERR_ADMINLOGOUT;
		else if(result == TrackSQL::AutoLogout)
			*Err = LOGINERR_AUTOLOGOUT;
		else
			*Err = LOGINERR_NOTLOGGEDIN;

		DEBUGMSG(("Login: attempt to play without logging in: %S",
				(const uint16*) m_pInfo->login));

		return false;
	}
	*/

    #ifdef FORCEDSHOE 
    m_pid=m_pInfo->login;
    #endif

	return true;
}

bool8
CPokerConnection::parseUnicodeCasinoLogin(int32* Err)
{
	int32 i=0;
    unsigned char byVer2;

    // First byte should be M for multilingual/unicode
    ASSERT( msgbuf[i] == 'M' ); i++;

    // Second byte should be L for login
    ASSERT( msgbuf[i] == 'L' ); i++;

	if (msgbuf[i++]	!= APP1				|| msgbuf[i++] != APP2 ||
		msgbuf[i++]	!= PROTO_VERSION1   || (byVer2 = msgbuf[i++]) > PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c %c%c. Looking for %c%c.%c%c",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4], APP1, APP2, PROTO_VERSION1, PROTO_VERSION2));
		return false;
	}

    //  kharmon.  09-28-05.  #5861.  Now include a machine id in the login message.
    if( byVer2 < PROTO_VERSION2)
    {
        m_eGameId = CARIBBEAN_STUD;
    }
    else if(byVer2 == PROTO_VERSION2)
    {
        m_eGameId = (CaribbeanGameId) msgbuf[i++];
    }

	// [AO 2008-03-28] Draw poker was missing
	if(m_eGameId != CARIBBEAN_STUD && m_eGameId != CARIBBEAN_HOLDEM && m_eGameId != CARIBBEAN_DRAW)
    {
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: invalid machine id: %d", (int) m_eGameId));
		return false;
    }

    m_pInfo = new CInfo();    
  
	if (!m_pInfo)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	i += m_pInfo->login.deserialize(msgbuf + i);

	m_pInfo->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( m_pInfo->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}
	else
	{	
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, m_pInfo->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					m_pInfo->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;				
				return false;
			}
		}
	}

    memset(m_pInfo->gid, 0xff, sizeof(CPokerGameID));
  
	// Create RTG Session
	if (tracker)
	{
		bool8 result;
		tracker->lock();
		result = tracker->createRTGSession(m_pInfo->forMoney, m_pInfo->login, m_pInfo->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			return false;
		}
	}

	m_pInfo->pid = m_pInfo->login;
	m_pDBInterface = getDBInterface(m_eGameId);
	
	/*
	m_pDBInterface = getDBInterface(m_eGameId);
	int32 result = m_pDBInterface->trackValidateLogin();
  
	if(m_pDBInterface==NULL || result != TrackSQL::NoError)
	{
		if(result == TrackSQL::AdminLogout)
			*Err = LOGINERR_ADMINLOGOUT;
		else if(result == TrackSQL::AutoLogout)
			*Err = LOGINERR_AUTOLOGOUT;
		else
			*Err = LOGINERR_NOTLOGGEDIN;

		DEBUGMSG(("Login: attempt to play without logging in: %S",
				(const uint16*) m_pInfo->login));

		return false;
	}
	*/

    #ifdef FORCEDSHOE 
    m_pid=m_pInfo->login;
    #endif

	return true;
}

int32
CPokerConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
CPokerConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

void
CPokerConnection::SendWinMail(char *pcGameName, int32 win, int32 jackpotAmount)
{
	String msg = "This is only a possible jackpot, as signaled by a casino server.\r\n"
				 "You will receive another email confirming or denying this potential jackpot.\r\n"
				 "Until then, this information is Bodog confidential, and not for release to clients or the public.\r\n"
				 "\r\nHere is the information about the potential win and winner - \r\n";

	String buffer;
	buffer.sprintf("Casino: %S\r\nPid: %S\r\n", (const uint16 *)ServerOpts::casinoName, (const uint16 *)m_pInfo->pid);
	msg += buffer;

	buffer.sprintf("Game: %s\r\n", pcGameName);
	msg += buffer;

	buffer.sprintf("Win: %d%% progressive\r\nGame num: %s\r\n", win, m_pInfo->gid);
	msg += buffer;

	buffer.sprintf("Amount: $%d.%02d\r\n", jackpotAmount/100, jackpotAmount%100);
	msg += buffer;

	SendJackpotCandidateMail(ServerOpts::smtphost,
							 pcGameName,
							 ServerOpts::casinoName,
			  				 msg,
			  				 getForRealFlag());
}

void CPokerConnection::trackLogoutSession()
{
	if (tracker && m_pInfo && !m_pInfo->login.isEmpty())
    {
        tracker->lock();
		tracker->logout(ServerOpts::casinoId, m_pInfo->login, m_pInfo->forMoney, String::emptyString, m_pInfo->sessionId);
        tracker->unlock();
    }
}